// 版权归 Epic Games, Inc. 所有，保留所有权利。

#include "LyraPerfStatWidgetBase.h"

#include "Engine/GameInstance.h"
#include "Performance/LyraPerformanceStatSubsystem.h"
#include "Styling/CoreStyle.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraPerfStatWidgetBase)

/**
 * SLyraLatencyGraph 的构造函数。
 * 初始化控件的各个属性。
 */
void SLyraLatencyGraph::Construct(const FArguments& InArgs)
{
	DesiredSize = InArgs._DesiredSize; // 设置期望尺寸
	MaxYAxisOfGraph = InArgs._MaxLatencyToGraph; // 设置Y轴最大值
	LineColor = InArgs._LineColor; // 设置线条颜色
	BackgroundColor = InArgs._BackgroundColor; // 设置背景颜色
}

/**
 * 绘制函数，负责绘制图表的背景和线条。
 */
int32 SLyraLatencyGraph::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect,
	FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	int32 MaxLayerId = LayerId; // 最大图层ID

	// 绘制背景
	FSlateDrawElement::MakeRotatedBox(
		OutDrawElements,
		MaxLayerId,
		AllottedGeometry.ToPaintGeometry(),
		FCoreStyle::Get().GetBrush("BlackBrush"), // 使用黑色画刷
		ESlateDrawEffect::NoPixelSnapping, // 无像素对齐效果
		0,
		TOptional<FVector2D>(),
		FSlateDrawElement::RelativeToElement,
		BackgroundColor); // 背景颜色

	// 我们需要在背景之上绘制图表，所以增加图层ID
	MaxLayerId++;

	// 实际绘制图表
	DrawTotalLatency(AllottedGeometry, OutDrawElements, MaxLayerId);
	
	MaxLayerId++;

	return MaxLayerId; // 返回最大图层ID
}

/**
 * 计算控件的期望尺寸。
 */
FVector2D SLyraLatencyGraph::ComputeDesiredSize(float LayoutScaleMultiplier) const
{
	return DesiredSize; // 返回期望尺寸
}

/**
 * 绘制总延迟图表。
 * 根据 GraphData 中的数据点绘制折线图。
 */
void SLyraLatencyGraph::DrawTotalLatency(const FGeometry& AllottedGeometry, FSlateWindowElementList& OutDrawElements, int32 LayerId) const
{
	if (!GraphData) // 如果图表数据无效则返回
	{
		return;
	}
	
	static TArray<FVector2D> Points; // 静态点数组，用于存储数据点
	Points.Reset(GraphData->GetSampleSize() + 1); // 重置点数组大小
	
	const FVector2D WidgetSize = AllottedGeometry.GetLocalSize(); // 获取控件局部尺寸
	const float LineThickness = 1.0f; // 线条厚度
	const double XSlice = WidgetSize.X / static_cast<double>(GraphData->GetSampleSize()); // X轴切片宽度
	const double Border = 1.0; // 边框

	int32 i = 0; // 索引
	
	GraphData->ForEachCurrentSample([&](const double Stat) // 遍历每个数据样本
	{
		double Y = WidgetSize.Y - FMath::Clamp((Stat * ScaleFactor), 0.0, MaxYAxisOfGraph) / MaxYAxisOfGraph * WidgetSize.Y; // 计算Y坐标
		Y = FMath::Clamp(Y, Border, WidgetSize.Y - Border); // 限制Y坐标在边框范围内

		Points.Emplace(XSlice * double(++i), Y); // 添加数据点
	});

	// 为什么这不只是画一条直线？？ 
	FSlateDrawElement::MakeLines( // 绘制线条
		OutDrawElements,
		LayerId,
		AllottedGeometry.ToPaintGeometry(),
		Points,
		ESlateDrawEffect::NoPixelSnapping, // 无像素对齐效果
		LineColor, // 线条颜色
		false,
		LineThickness); // 线条厚度
}

//////////////////////////////////////////////////////////////////////
// ULyraPerfStatGraph

/**
 * ULyraPerfStatGraph 的构造函数。
 * 设置裁剪方式为裁剪到边界。
 */
ULyraPerfStatGraph::ULyraPerfStatGraph(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer) // 调用父类构造函数
{
	SetClipping(EWidgetClipping::ClipToBounds); // 设置裁剪方式
}

/**
 * 重建控件，创建 SLyraLatencyGraph 实例。
 */
TSharedRef<SWidget> ULyraPerfStatGraph::RebuildWidget()
{
	return SAssignNew(SlateLatencyGraph, SLyraLatencyGraph); // 创建并分配 Slate 延迟图表控件
}

/**
 * 释放 Slate 资源。
 */
void ULyraPerfStatGraph::ReleaseSlateResources(bool bReleaseChildren)
{
	Super::ReleaseSlateResources(bReleaseChildren); // 调用父类释放资源
	SlateLatencyGraph.Reset(); // 重置 Slate 延迟图表控件
}

/**
 * 设置图表线条颜色。
 */
void ULyraPerfStatGraph::SetLineColor(const FColor& InColor)
{
	SlateLatencyGraph->SetLineColor(InColor); // 设置线条颜色
}

/**
 * 设置图表Y轴最大值。
 */
void ULyraPerfStatGraph::SetMaxYValue(const float InValue)
{
	SlateLatencyGraph->SetMaxYValue(InValue); // 设置Y轴最大值
}

/**
 * 设置图表背景颜色。
 */
void ULyraPerfStatGraph::SetBackgroundColor(const FColor& InValue)
{
	SlateLatencyGraph->SetBackgroundColor(InValue); // 设置背景颜色
}

/**
 * 更新图表数据。
 */
void ULyraPerfStatGraph::UpdateGraphData(const FSampledStatCache* StatData, const float ScaleFactor)
{
	SlateLatencyGraph->UpdateGraphData(StatData, ScaleFactor); // 更新图表数据
}

//////////////////////////////////////////////////////////////////////
// ULyraPerfStatWidgetBase

/**
 * 获取统计值。
 * 从统计子系统中获取缓存的统计值。
 */
double ULyraPerfStatWidgetBase::FetchStatValue()
{
	if (ULyraPerformanceStatSubsystem* Subsystem = GetStatSubsystem()) // 如果统计子系统有效
	{
		return CachedStatSubsystem->GetCachedStat(StatToDisplay); // 返回缓存的统计值
	}
	else
	{
		return 0.0; // 返回默认值
	}
}

/**
 * 更新图表数据。
 * 如果存在图表控件，则从统计子系统获取数据并更新图表。
 */
void ULyraPerfStatWidgetBase::UpdateGraphData(const float ScaleFactor)
{
	// 当我们缓存子系统时，如果存在图表控件，也更新图表数据指针
	if (PerfStatGraph) // 如果性能统计图表控件有效
	{
		if (const FSampledStatCache* GraphData = CachedStatSubsystem->GetCachedStatData(StatToDisplay)) // 如果获取到图表数据
		{
			PerfStatGraph->UpdateGraphData(GraphData, ScaleFactor); // 更新图表数据
		}	
	}
}

/**
 * 原生构造函数。
 * 缓存统计子系统并初始化图表控件属性。
 */
void ULyraPerfStatWidgetBase::NativeConstruct()
{
	Super::NativeConstruct(); // 调用父类的 NativeConstruct

	// 在构造时缓存子系统，这也会确保图表是最新的
	GetStatSubsystem(); // 获取统计子系统
	
	if (PerfStatGraph) // 如果性能统计图表控件有效
	{
		PerfStatGraph->SetLineColor(GraphLineColor); // 设置图表线条颜色
		PerfStatGraph->SetMaxYValue(GraphMaxYValue); // 设置图表Y轴最大值
		PerfStatGraph->SetBackgroundColor(GraphBackgroundColor); // 设置图表背景颜色
	}
}

/**
 * 获取统计子系统。
 * 从游戏实例中获取 ULyraPerformanceStatSubsystem 实例。
 */
ULyraPerformanceStatSubsystem* ULyraPerfStatWidgetBase::GetStatSubsystem()
{
	if (CachedStatSubsystem == nullptr) // 如果缓存的统计子系统为空
	{
		if (UWorld* World = GetWorld()) // 如果获取到世界上下文
		{
			if (UGameInstance* GameInstance = World->GetGameInstance()) // 如果获取到游戏实例
			{
				CachedStatSubsystem = GameInstance->GetSubsystem<ULyraPerformanceStatSubsystem>(); // 获取统计子系统
			}
		}
	}

	return CachedStatSubsystem; // 返回缓存的统计子系统
}